package com.lightbend.akka.sample

import java.util.concurrent.TimeUnit

import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
import akka.actor.typed.{ActorRef, Behavior, PostStop, Signal}
import akka.util.Timeout
import com.lightbend.akka.sample.DeviceGroup.DeviceTerminated
import com.lightbend.akka.sample.DeviceManager.{DeviceRegistered, ReplyDeviceList, RequestDeviceList, RequestTrackDevice}

object DeviceGroup{
  def apply(groupId:String): Behavior[Command] = Behaviors.setup(context => new DeviceGroup(context, groupId))

  trait Command

  private final case class DeviceTerminated(device:ActorRef[Device.Command], groupId:String, deviceId:String) extends Command

}

class DeviceGroup(context: ActorContext[DeviceGroup.Command], groupId: String) extends AbstractBehavior[DeviceGroup.Command](context){

  private var deviceIdToActor = Map.empty[String, ActorRef[Device.Command]]

  context.log.info("DeviceGroup {} started", groupId)

  override def onMessage(msg: DeviceGroup.Command): Behavior[DeviceGroup.Command] = {
    implicit val timeout: Timeout = new Timeout(2, TimeUnit.MINUTES)
    msg match {
      case trackMsg @ RequestTrackDevice(groupId, deviceId, replyTo) =>
        deviceIdToActor.get(deviceId) match {
          case Some(deviceActor) => replyTo ! DeviceRegistered(deviceActor)
          case None =>
            context.log.info("Creating device actor for {}", trackMsg.deviceId)
            val deviceActor = context.spawn(Device(groupId, deviceId), s"device-$deviceId")
            context.watchWith(deviceActor, DeviceTerminated(deviceActor, groupId, deviceId))
            deviceIdToActor += deviceId -> deviceActor
            replyTo ! DeviceRegistered(deviceActor)
        }
        this
      case RequestTrackDevice(gId, _, _) =>
        context.log.warn("Ignoring TrackDevice request for {}, this actor is responsible for {}.", gId, groupId)
        this
      case DeviceTerminated(deviceActor, _, deviceId) =>
        context.log.info("Device actor for {} has been terminated", deviceId)
        deviceIdToActor -= deviceId
        this
      case RequestDeviceList(requestId, gId, replyTo) =>
        if(gId == groupId){
          replyTo ! ReplyDeviceList(requestId, deviceIdToActor.keySet)
          this
        }else Behaviors.unhandled[DeviceGroup.Command]
    }
  }

  override def onSignal: PartialFunction[Signal, Behavior[DeviceGroup.Command]] = {
    case stop: PostStop =>
      context.log.info("DeviceGroup {} stopped", groupId)
      this
  }
}


